package rpc_client

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"git.mycaigou.com/gfyx/common/authorize"
	"git.mycaigou.com/gfyx/common/usermetadata"
	"git.myscrm.cn/golang/common/ykerrcode"
	"time"

	"git.mycaigou.com/ycg/stark-extend/go2skyplugins/go2skygrpc"
	"git.myscrm.cn/golang/stark/v4"
	"git.myscrm.cn/golang/stark/v4/module/client_conn"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

type RpcClientFn func(ctx context.Context, clientConn *grpc.ClientConn) (interface{}, error)

type GrpcClient struct {
	client     *grpc.ClientConn
	ctx        context.Context
	errs       []error
	tenantCode string
}

//type AuthInfo struct {
//	AppId     string `json:"app_id"`
//	TimeStamp int64  `json:"time_stamp"`
//}

// New
// 构造函数
func New(ctx context.Context) *GrpcClient {
	return &GrpcClient{
		ctx: ctx,
	}
}

// Do
// 执行任务
func (c *GrpcClient) Do(fn RpcClientFn, v interface{}, params interface{}) (errs []error) {
	defer func() {
		if msg := recover(); msg != nil {
			stark.Logger.Errorf(c.ctx, "rpc client Do error: %+v", msg)
			err := ykerrcode.TogRPCError(ykerrcode.FAIL, fmt.Sprintf("rpc client Do error: %v", msg))
			c.errs = append(c.errs, err)
			//errs = append(errs, err)
			errs = c.errs
		}
		if c.client != nil {
			err := c.client.Close()
			if err != nil {
				stark.Logger.Errorf(c.ctx, "close client Close error: %+v", err)
				c.errs = append(c.errs, err)
				//errs = append(errs, err)
				errs = c.errs
			}
		}
	}()

	if fn != nil {
		stark.Logger.Infof(c.ctx, "(GrpcClient-do-request-before) params: %+v", params)
		t, err := fn(c.ctx, c.client)
		if err != nil {
			c.errs = append(c.errs, err)
			return c.errs
		}

		tByte, err := json.Marshal(t)
		if err != nil {
			c.errs = append(c.errs, err)
			return c.errs
		}

		err = json.Unmarshal(tByte, v)
		if err != nil {
			c.errs = append(c.errs, err)
			return c.errs
		}

		stark.Logger.Infof(c.ctx, "(GrpcClient-do-request-after) res: %+v, v: %+v, err: %+v", t, v, err)
	}
	return errs
}

// 获取租户级别rpc客户端
func (c *GrpcClient) GetTenantClientConn(serviceName string) *GrpcClient {
	contextTenantCode := authorize.GetTenantCode(c.ctx)
	if contextTenantCode == "" {
		err := ykerrcode.TogRPCError(ykerrcode.ACCESS_DENIED, "上下文缺少租户号")
		stark.Logger.Error(c.ctx, "clientConn GetTenantClientConn err", err)
		c.errs = append(c.errs, err)
		// 注意，这里不要，否则错误会别吞掉 return c
	}
	c.tenantCode = contextTenantCode

	return c.clientConn(serviceName)
}

// GetClientConn
// 获取rpc client对象
func (c *GrpcClient) GetClientConn(serviceName string) *GrpcClient {
	return c.clientConn(serviceName)
}

func (c *GrpcClient) clientConn(serviceName string) *GrpcClient {
	c.pairsMetaData(serviceName)
	if len(c.errs) > 0 {
		stark.Logger.Error(c.ctx, "clientConn pairsMetaData err", c.errs)
		// 这里不要return，否则鉴权错误会被吞掉
	}

	conn, err := client_conn.NewConn(serviceName)
	if err != nil {
		c.errs = append(c.errs, err)
		stark.Logger.Error(c.ctx, "clientConn NewConn err", c.errs)
		return c
	}
	conn = conn.WithChainUnaryInterceptor(go2skygrpc.NewUnaryClientInterceptor())

	c.client, err = conn.GetAPMConn(c.ctx)
	if err != nil {
		c.errs = append(c.errs, err)
		stark.Logger.Error(c.ctx, "clientConn GetAPMConn err", c.errs)
		return c
	}

	return c
}

// HmacSha256
// hmacsha256验证
func (c *GrpcClient) hmacSha256(src, key string) (string, error) {
	m := hmac.New(sha256.New, []byte(key))
	_, err := m.Write([]byte(src))
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(m.Sum(nil)), nil
}

// pairsMetaData
// 生成rpc签名字符串
func (c *GrpcClient) pairsMetaData(serviceName string) *GrpcClient {
	appId := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(serviceName+".appId", "")
	secret := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(serviceName+".secret", "")
	if appId == "" || secret == "" {
		c.errs = append(c.errs, errors.New("appid or serect is null"))
		return c
	}

	var authInfo AuthInfo
	authInfo.AppId = appId
	authInfo.TimeStamp = time.Now().Unix()
	str := fmt.Sprintf("%s&%d&%s", authInfo.AppId, authInfo.TimeStamp, secret)
	sign, err := c.hmacSha256(str, secret)
	if err != nil {
		c.errs = append(c.errs, err)
		return c
	}

	a, err := json.Marshal(authInfo)
	if err != nil {
		c.errs = append(c.errs, err)
		return c
	}

	md := metadata.Pairs("authorization", "bearer "+fmt.Sprintf("%s.%s", sign, string(a)))
	c.ctx = metadata.NewOutgoingContext(c.ctx, md)
	if c.tenantCode != "" {
		c.ctx = metadata.AppendToOutgoingContext(c.ctx, "tenant_code", c.tenantCode)
	}

	// 携带用户信息传递到下游
	c.ctx, err = usermetadata.SetUserInfoToMeta(c.ctx)
	if err != nil {
		c.errs = append(c.errs, err)
		return c
	}

	return c
}
